//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Apache License, Version 2.0.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************

namespace Microsoft.Research.ScientificWorkflow.UIDesigner
{
    using System;
    using System.Collections;
    using Microsoft.Research.ScientificWorkflow.TridentModel;
    using Microsoft.Research.ScientificWorkflow.TridentUtilities;
    using System.Collections.Generic;
    using System.Windows;
    using System.Windows.Input;

    public class TridentUIGenerationVisitor : TridentBaseVisitor
    {
        private ComposerRenderer composerRenderer;
        private Hashtable modelToElementHash = new Hashtable();
        private Dictionary<String, DataTypeModel> dataTypes;
        private Dictionary<string, ITridentUIElement> uIElements = new Dictionary<string, ITridentUIElement>();
        private Stack<BaseCompositeModel> compositeModelStack = new Stack<BaseCompositeModel>();
        private bool isProvenanceMode;

        public override BaseCompositeModel CurrentCompositeModel
        {
            get
            {
                BaseCompositeModel compModel = null;
                if (this.compositeModelStack.Count > 0)
                {
                    compModel = this.compositeModelStack.Peek();
                }

                return compModel;
            }
        }

        public ITridentUIElement GenerateUIRepresentation(BaseModel model, Dictionary<String, DataTypeModel> dataTypes,
            Dictionary<string, ITridentUIElement> uiElements, ComposerRenderer composerRenderHelper)
        {
            ITridentUIElement uiElem = null;
            this.dataTypes = dataTypes;
            this.uIElements = uiElements;
            this.composerRenderer = composerRenderHelper;
            this.modelToElementHash.Clear();
            this.Walker(model);
            if (this.modelToElementHash.Contains(model))
            {
                uiElem = this.modelToElementHash[model] as ITridentUIElement;
            }

            // Update the In Used data type to reflect the new data types present in the activity.
            this.composerRenderer.UpdateDataTypes(model);

            return uiElem;
        }

        public override void VisitCompositeActivityModel(CompositeActivityModel tridentCompositeActivityModel)
        {
            if (TridentUIGenerationVisitor.AllowUiRender(tridentCompositeActivityModel))
            {
                CompositeActivityElement compositeActivityElement = null;
                if (tridentCompositeActivityModel.ActivityClass == UIDesignerCodeConstants.ACTIVITYCLASSIFCONDITION)
                {
                    compositeActivityElement = new ConditionConnectorActivityElement(tridentCompositeActivityModel, this.dataTypes);
                }
                else
                {
                    compositeActivityElement = new CompositeActivityElement(tridentCompositeActivityModel, this.dataTypes);
                }

                this.modelToElementHash[tridentCompositeActivityModel] = compositeActivityElement;
                this.AddTridentUiElementToTree(compositeActivityElement);
                if (tridentCompositeActivityModel.ActivityClass.Equals("System.Workflow.Activities.ParallelActivity", StringComparison.OrdinalIgnoreCase))
                {
                    (compositeActivityElement as FrameworkElement).Loaded += delegate { compositeActivityElement.UpdateBranchPositions(); };
                    (compositeActivityElement as FrameworkElement).SizeChanged += delegate { compositeActivityElement.UpdateBranchPositions(); };
                }
            }
        }

        public override void VisitSimpleActivityModel(SimpleActivityModel tridentSimpleActivityModel)
        {
            if (TridentUIGenerationVisitor.AllowUiRender(tridentSimpleActivityModel))
            {
                if (tridentSimpleActivityModel.ParentModel != null && tridentSimpleActivityModel.ParentModel.IsCompositeActivityModel && tridentSimpleActivityModel.ParentModel.ActivityClass.Equals(UIDesignerCodeConstants.ACTIVITYCLASSIFCONDITION))
                {
                    return;
                }

                // Leaf node activity
                WorkflowActivityElement inner = new WorkflowActivityElement(tridentSimpleActivityModel, this.dataTypes);
                this.modelToElementHash[tridentSimpleActivityModel] = inner;
                this.AddTridentUiElementToTree(inner);
            }
        }

        public override void VisitSubsectionModel(TridentSubsectionModel tridentSubsectionModel)
        {
            if (TridentUIGenerationVisitor.AllowUiRender(tridentSubsectionModel))
            {
                SubsectionCollapsedCtrl collapsedCtrl = new SubsectionCollapsedCtrl(tridentSubsectionModel);
                SubsectionExpandedCtrl expandedCtrl = new SubsectionExpandedCtrl(tridentSubsectionModel);
                collapsedCtrl.ExpandedCtrl = expandedCtrl;
                expandedCtrl.CollapsedCtrl = collapsedCtrl;

                ITridentUIElement subsectionCtrl = null;
                if (tridentSubsectionModel.IsCollapsed)
                {
                    subsectionCtrl = collapsedCtrl;
                    if (this.composerRenderer != null)
                    {
                        this.composerRenderer.InitializeTridentUIElement(expandedCtrl);
                    }
                }
                else
                {
                    subsectionCtrl = expandedCtrl;
                    if (this.composerRenderer != null)
                    {
                        this.composerRenderer.InitializeTridentUIElement(collapsedCtrl);
                    }
                }

                this.modelToElementHash[tridentSubsectionModel] = expandedCtrl;
                this.AddTridentUiElementToTree(subsectionCtrl);
            }
        }

        public override void VisitForActivityModel(ForActivityCompositeModel tridentForActivityCompositeModel)
        {
            if (TridentUIGenerationVisitor.AllowUiRender(tridentForActivityCompositeModel))
            {
                ForActivityUIElement forActivity = new ForActivityUIElement(tridentForActivityCompositeModel, this.dataTypes);
                this.modelToElementHash[tridentForActivityCompositeModel] = forActivity;
                this.AddTridentUiElementToTree(forActivity);                
            }
        }

        public override void VisitReplicatorActivityModel(ReplicatorActivityCompositeModel tridentReplicatorActivityCompositeModel)
        {
            if (TridentUIGenerationVisitor.AllowUiRender(tridentReplicatorActivityCompositeModel))
            {
                ReplicatorActivityUIElement replicatorActivity = new ReplicatorActivityUIElement(tridentReplicatorActivityCompositeModel, this.dataTypes);
                this.modelToElementHash[tridentReplicatorActivityCompositeModel] = replicatorActivity;
                this.AddTridentUiElementToTree(replicatorActivity);
            }
        }

        public override void VisitIfElseActivityModel(IfElseActivityCompositeModel tridentIfElseActivityCompositeModel)
        {
            if (TridentUIGenerationVisitor.AllowUiRender(tridentIfElseActivityCompositeModel))
            {
                IFElseUIElement ifElseElement = new IFElseUIElement(tridentIfElseActivityCompositeModel, this.dataTypes);
                this.modelToElementHash[tridentIfElseActivityCompositeModel] = ifElseElement;
                this.AddTridentUiElementToTree(ifElseElement);
                (ifElseElement as FrameworkElement).Loaded += delegate { ifElseElement.ArrangeBranches(); };
            }
        }

        public override void VisitSimpleBlackboxModel(SimpleBlackboxModel simpleBlackBoxModel)
        {
            // Leaf node activity
            SimpleBlackboxActivityElement inner = new SimpleBlackboxActivityElement(simpleBlackBoxModel, this.dataTypes);
            this.modelToElementHash[simpleBlackBoxModel] = inner;
            this.AddTridentUiElementToTree(inner);
        }

        public override void VisitCompositeBlackboxModel(CompositeBlackboxModel compositeBlackboxModel)
        {
            // Composite blackbox activity.
            CompositeBlackboxActivityElement composite = new CompositeBlackboxActivityElement(compositeBlackboxModel);
            this.modelToElementHash[compositeBlackboxModel] = composite;
            this.AddTridentUiElementToTree(composite);
        }


        private void AddTridentUiElementToTree(ITridentUIElement elem)
        {
            this.uIElements[elem.Model.UniqueId] = elem;
            if (this.composerRenderer != null)
            {
                this.composerRenderer.InitializeTridentUIElement(elem);
            }

            BaseCompositeModel compositeModel = this.CurrentCompositeModel;
            if (compositeModel != null && this.modelToElementHash.Contains(compositeModel))
            {
                ICompositeUIElement currentCompositeElem = this.modelToElementHash[compositeModel] as ICompositeUIElement;
                if (currentCompositeElem != null)
                {
                    currentCompositeElem.AddChildElement(elem);
                }
            }
        }

        private static bool AllowUiRender(BaseModel model)
        {
            bool allowRender = true;
            if (model.IsSimpleActivityModel)
            {
                allowRender = (model.VisibleOnUI.ActivityVisibility == Visibility.Visible) ? true : false;
            }
            else if (model.IsCompositeActivityModel)
            {
                allowRender = (model.VisibleOnUI.ActivityVisibility == Visibility.Visible) ? true : false;
            }

            return allowRender;
        }

        protected override void Walker(BaseModel model)
        {
            if (model.IsProvenanceMode)
                isProvenanceMode = true;

            model.IsProvenanceMode = isProvenanceMode;

            model.Accept(this);
            BaseCompositeModel compositeModel = model as BaseCompositeModel;
            if (compositeModel != null)
            {
                bool allowsRender = TridentUIGenerationVisitor.AllowUiRender(model);
                if (allowsRender)
                {
                    this.compositeModelStack.Push(compositeModel);
                }

                for (int iterator = 0; iterator < compositeModel.Children.Count; iterator++)
                {
                    this.Walker(compositeModel.Children[iterator]);
                }

                if (allowsRender)
                {
                    this.compositeModelStack.Pop();
                }
            }
        }
    }
}
